package com.ksyun.onepass.service.impl;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ksyun.onepass.domain.po.Charge;
import com.ksyun.onepass.domain.po.FinishResponse;
import com.ksyun.onepass.domain.po.Response;
import com.ksyun.onepass.domain.po.User;
import com.ksyun.onepass.service.BalanceService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;

@Slf4j
@Service
@RequiredArgsConstructor
public class BalanceServiceImpl implements BalanceService {

    @Resource
    private final RedisTemplate<String , String> redisTemplate;

    public void message(String batchPayId) throws Exception{
        String requestId = UUID.randomUUID().toString();
        ObjectMapper objectMapper = new ObjectMapper();
        FinishResponse response1=new FinishResponse(batchPayId);
        String json = objectMapper.writeValueAsString(response1);
        String url = "http://172.16.0.90/thirdpart/onePass/batchPayFinish";
        HttpPost httpPost = new HttpPost(url);
        StringEntity entity = new StringEntity(json);
        httpPost.setEntity(entity);
        httpPost.setHeader("Accept", "application/json");
        httpPost.setHeader("Content-Type", "application/json");
        httpPost.setHeader("X-KSY-KINGSTAR-ID", "20010");
        httpPost.setHeader("X-KSY-REQUEST-ID", requestId);
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse response = httpClient.execute(httpPost);
        response.close();
        httpClient.close();
    }

    private static final BigDecimal INITIAL_AMOUNT = new BigDecimal("10000.00"); // 初始充值金额
    private static final BigDecimal MIN_AMOUNT = new BigDecimal("0.01"); // 最小充值金额
    @Override
    public void batchPay(List<Long> uids,String batchPayId) {
        //首先获取到uids，接下来多线程调用远程的方法
        /*System.out.println(uids);*/
        for (Long uid : uids) {
            try {
                User user = rechargeTest(uid); // 远程调用获取用户信息
                //存入Redis当中
                if (user != null) {
                    redisTemplate.opsForValue().set(user.getUid().toString(),user.getAmount().toString());
                }
            } catch (Exception e) {
            }
        }
        try {
            message(batchPayId);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    public User rechargeTest(Long uid) throws Exception {
        ExecutorService executor = Executors.newFixedThreadPool(128); // 创建线程池
        /*AtomicLong totalRecharged = new AtomicLong(0); // 使用AtomicLong处理总充值金额*/
        List<Future<?>> futures = new ArrayList<>(); // 未来对象列表
        AtomicReference<BigDecimal> totalRecharged = new AtomicReference<>(BigDecimal.ZERO);
        // 定义任务
        Runnable rechargeTask = () -> {
            CloseableHttpClient httpClient = HttpClients.createDefault();
            BigDecimal localAmount = INITIAL_AMOUNT; // 每个线程独立的金额
            while (true) {
                String requestId = UUID.randomUUID().toString();
                HttpPost httpPost = null;
                BigDecimal init=localAmount;
                try {
                    httpPost = createHttpPost(requestId, localAmount, uid);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
                boolean success = false;

                for (int attempt = 0; attempt < 10; attempt++) {
                    try {
                        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
                            HttpEntity responseEntity = response.getEntity();
                            if (responseEntity != null) {
                                String responseBody = EntityUtils.toString(responseEntity);
                                ObjectMapper mapper = new ObjectMapper();
                                JsonNode jsonNode = mapper.readTree(responseBody);
                                int statusCode = jsonNode.get("code").asInt();

                                if (statusCode == 404) {
                                    // 用户不存在，退出
                                    return;
                                }

                                if (statusCode == 200) {
                                    totalRecharged.getAndAccumulate(localAmount, BigDecimal::add); // 成功，增加已充值金额
                                    success = true;
                                    break; // 成功，跳出重试循环
                                } else if (statusCode == 501) {
                                    // 失败，减半金额
                                    localAmount = localAmount.divide(new BigDecimal("2"), 2, RoundingMode.HALF_UP);
                                    if (localAmount.compareTo(MIN_AMOUNT) < 0) {
                                        localAmount = MIN_AMOUNT; // 确保金额不小于0.01
                                    }
                                }
                            }
                        }
                        break; // 请求成功，跳出重试循环
                    } catch (SocketTimeoutException e) {
                        // 请求超时，继续尝试重试
                    } catch (Exception e) {
                        e.printStackTrace(); // 捕获其他异常
                    }
                }

                if (init.compareTo(MIN_AMOUNT) == 0 && !success) {
                    break; // 如果金额降到MIN_AMOUNT且充值失败，则结束循环
                }
            }
            try {
                httpClient.close(); // 确保在每个线程结束时关闭HTTP客户端
            } catch (Exception e) {
                e.printStackTrace();
            }
        };

        // 启动多个线程进行充值
        for (int i = 0; i < 128; i++) {
            futures.add(executor.submit(rechargeTask));
        }

        executor.shutdown(); // 不再接受新任务
        try {
            if (!executor.awaitTermination(10, TimeUnit.MINUTES)) {
                executor.shutdownNow(); // 超过10分钟，强制关闭
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
        }
        /*// 输出最终的充值总金额
        System.out.println("成功充值总金额: " + totalRecharged.get());*/
        return new User(uid, totalRecharged.get());
    }

    private HttpPost createHttpPost(String requestId, BigDecimal amount,Long uid) throws Exception{
        RequestConfig requestConfig = RequestConfig.custom()
                .setSocketTimeout(150)
                .build();
        String transactionId = UUID.randomUUID().toString();
        ObjectMapper objectMapper = new ObjectMapper();
        Charge charge = new Charge(transactionId, uid, amount);
        String json = objectMapper.writeValueAsString(charge);
        String url = "http://172.16.0.90/thirdpart/onePass/pay";
        HttpPost httpPost = new HttpPost(url);
        StringEntity entity = new StringEntity(json);
        httpPost.setEntity(entity);
        httpPost.setHeader("Accept", "application/json");
        httpPost.setHeader("Content-Type", "application/json");
        httpPost.setHeader("X-KSY-KINGSTAR-ID", "20010");
        httpPost.setHeader("X-KSY-REQUEST-ID", requestId);
        httpPost.setConfig(requestConfig);
        return httpPost;
    }

    // 自定义的线程安全BigDecimal类
    public static class AtomicBigDecimal {
        private final AtomicReference<BigDecimal> value;

        public AtomicBigDecimal(BigDecimal initialValue) {
            value = new AtomicReference<>(initialValue);
        }

        public BigDecimal get() {
            return value.get();
        }

        public void add(BigDecimal delta) {
            while (true) {
                BigDecimal current = value.get();
                BigDecimal next = current.add(delta);
                if (value.compareAndSet(current, next)) {
                    break;
                }
            }
        }
    }
}
